package com.songtech.uitest.util;

import com.songtech.uitest.components.ScreenShoter;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;

import java.util.ArrayList;
import java.util.List;

/**
 * 为了避免测试方法一旦遇到断言失败就停止继续运行，特加此类
 */
public class AssertionListener extends TestListenerAdapter {

    private static ITestResult itr = null;

    private Logger logger = LoggerFactory.getLogger(AssertionListener.class);

    //这个方法只会执行@Test的测试方法，什么teardown之类的不会执行，retry时也会执行
    @Override
    public void onTestStart(ITestResult tr) {
        //如果是带着错误来的，说明上一个测试方法有出错，或者是在重试本测试方法
        if(!Assertion.errors.isEmpty() && itr != null){
            tr.getTestContext().getPassedTests().removeResult(itr);
            itr.setStatus(ITestResult.FAILURE);
            tr.getTestContext().getFailedTests().addResult(itr, itr.getMethod());

        }

        Assertion.flag = true;
        Assertion.fatal = false;
        Assertion.errors.clear(); //该测试方法完了后清空断言错误list
    }

    //一遇到断言错误或抛出异常就会执行这个，并且tr的状态已经是failure了。
    // 但因为我们自己写了个Assertion把断言异常捕获掉了，
    // 所以现在任何断言错误都不会走进这个方法，而是会进入onTestSuccess。
    //若测试方法报了其它非断言异常，此时若有重试，则会进入onTestSkipped方法
    //重试机会完了都还是失败，才会进入此方法。
    @Override
    public void onTestFailure(ITestResult tr) {

    }

    //重试时，也会调用这个方法
    @Override
    public void onTestSkipped(ITestResult tr){

    }

    //因为我们自己写了个Assertion把断言异常捕获掉了，
    //所以只要不是致命fatal错误，都会在整个测试方法执行完后，进入到此方法里来。
    @Override
    public void onTestSuccess(ITestResult tr) {
//        System.out.println("=========================测试方法算作成功了。");
        //如果该测试方法里出现了断言错误，保留当前的ITestResult
        if(!Assertion.errors.isEmpty()){
            itr = tr;
        }

    }

    //这是在失败了，且本测试方法的注解里有successPercentage且错误没超标时，才会进入此方法
    @Override
    public void onTestFailedButWithinSuccessPercentage(ITestResult tr){

    }





    private int index = 0;
    //废弃没有使用
    private void handleAssertion(ITestResult tr){
        //如果有错误发生
        if(!Assertion.flag){

            Throwable throwable = tr.getThrowable();
            if(throwable==null){
                throwable = new Throwable();
            }
            StackTraceElement[] traces = throwable.getStackTrace();
            StackTraceElement[] alltrace = new StackTraceElement[0];
            for (Error e : Assertion.errors) {
                StackTraceElement[] errorTraces = e.getStackTrace();
                StackTraceElement[] et = this.getKeyStackTrace(tr, errorTraces);
                StackTraceElement[] message = new StackTraceElement[]{new StackTraceElement("message : "+e.getMessage()+" in method : ", tr.getMethod().getMethodName(), tr.getTestClass().getRealClass().getSimpleName(), index)};
                index = 0;
                alltrace = this.merge(alltrace, message);
                alltrace = this.merge(alltrace, et);
            }
            if(traces!=null){
                traces = this.getKeyStackTrace(tr, traces);
                alltrace = this.merge(alltrace, traces);
            }
            throwable.setStackTrace(alltrace);

            //注释掉是为了打印出所有出错堆栈
            //tr.setThrowable(throwable);

            //若致命错误则视为出错
            if(Assertion.fatal){
                tr.setStatus(ITestResult.FAILURE);
            }
            else{ //若非致命错误则视为部分出错，可以继续
                tr.setStatus(ITestResult.SUCCESS_PERCENTAGE_FAILURE);
            }

        }
    }

    private StackTraceElement[] getKeyStackTrace(ITestResult tr, StackTraceElement[] stackTraceElements){
        List<StackTraceElement> ets = new ArrayList<StackTraceElement>();
        for (StackTraceElement stackTraceElement : stackTraceElements) {
//            if(stackTraceElement.getClassName().equals(tr.getTestClass().getName())){
                ets.add(stackTraceElement);
                index = stackTraceElement.getLineNumber();
//            }
        }
        StackTraceElement[] et = new StackTraceElement[ets.size()];
        for (int i = 0; i < et.length; i++) {
            et[i] = ets.get(i);
        }
        return et;
    }

    private StackTraceElement[] merge(StackTraceElement[] traces1, StackTraceElement[] traces2){
        StackTraceElement[] ste = new StackTraceElement[traces1.length+traces2.length];
        for (int i = 0; i < traces1.length; i++) {
            ste[i] = traces1[i];
        }
        for (int i = 0; i < traces2.length; i++) {
            ste[traces1.length+i] = traces2[i];
        }
        return ste;
    }
}
